package org.artifact.core.plugin.rpc.client;

import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.artifact.core.plugin.rpc.pojo.RpcRequest;
import org.artifact.core.plugin.rpc.pojo.RpcResponse;
import org.artifact.core.plugin.rpc.registry.ServiceDiscovery;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * RPC 代理（用于创建 RPC 服务代理）
 *
 * @author huangyong
 * @since 1.0.0
 */
public class RpcProxy {

    private static final Log log = LogFactory.get(RpcProxy.class);

    private String serviceAddress;

    private ServiceDiscovery serviceDiscovery;

    public static Map<String, RpcClient> clients = new ConcurrentHashMap<>();

    public RpcProxy(String serviceAddress) {
        this.serviceAddress = serviceAddress;
    }

    public RpcProxy(ServiceDiscovery serviceDiscovery) {
        this.serviceDiscovery = serviceDiscovery;
    }

    @SuppressWarnings("unchecked")
    public <T> T create(final Class<?> interfaceClass) {
        return create(interfaceClass, "");
    }

    @SuppressWarnings("unchecked")
    public <T> T create(final Class<?> interfaceClass, final String serviceVersion) {

        // 创建动态代理对象
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 创建 RPC 请求对象并设置请求属性
                        RpcRequest request = new RpcRequest();
                        request.setRequestId(UUID.randomUUID().toString());
                        request.setInterfaceName(method.getDeclaringClass().getName());
                        request.setServiceVersion(serviceVersion);
                        request.setMethodName(method.getName());
                        request.setParameterTypes(method.getParameterTypes());
                        request.setParameters(args);


                        RpcResponse response = sendRequest(0,request, interfaceClass, serviceVersion);


                        // 返回 RPC 响应结果
                        if (response.hasException()) {
                            throw response.getException();
                        } else {
                            return response.getResult();
                        }
                    }
                }
        );
    }

    public RpcResponse sendRequest(int index, RpcRequest request, final Class<?> interfaceClass, final String serviceVersion) throws Throwable {
        try {
            // 获取 RPC 服务地址
            if (serviceDiscovery != null) {
                String serviceName = interfaceClass.getName();
                if (StrUtil.isNotEmpty(serviceVersion)) {
                    serviceName += "-" + serviceVersion;
                }
                serviceAddress = serviceDiscovery.discover(serviceName);
                log.debug("discover service: {} => {}", serviceName, serviceAddress);
            }
            if (StrUtil.isEmpty(serviceAddress)) {
                throw new RuntimeException("server address is empty");
            }
            // 从 RPC 服务地址中解析主机名与端口号
            String[] array = StrUtil.split(serviceAddress, ":");
            String host = array[0];
            int port = Integer.parseInt(array[1]);
            // 创建 RPC 客户端对象并发送 RPC 请求
            String key = host + ":" + port;
            RpcClient client = clients.computeIfAbsent(key, k -> new RpcClient(host, port));

            long time = System.currentTimeMillis();
            RpcResponse response = client.send(request);
            log.debug("time: {}ms", System.currentTimeMillis() - time);
            if (response == null) {
                throw new RuntimeException("response is null");
            }
            return response;
        } catch (Exception e) {
            log.error(e);
            // 失败重试
            if (index < 3){
                return sendRequest(index+1,request,interfaceClass,serviceVersion);
            }else {
                throw new RuntimeException("rpc send error");
            }
        }
    }
}
